#include "miniutil/os_compatible.h"
#ifndef WIN32
#include <cstring>
#endif
#include "miniutil/pickle.h"

/*
    091215 port from frame/fmmsglinker, not test..
*/
namespace miniutil { namespace frame {

    miniutil::m_uint16_t get_uint16_be(const char* buf)
    {
        miniutil::m_uint16_t ret;
        unsigned char *tmp;

        tmp = (unsigned char *) buf;

        ret = tmp[0] & 0xff;
        ret = (ret << 8) + (tmp[1] & 0xff);

        return ret;
    }

    miniutil::m_uint32_t get_uint32_be(const char* buf)
    {
        miniutil::m_uint32_t ret;
        unsigned char *tmp;

        tmp = (unsigned char *) buf;

        ret = tmp[0] & 0xff;
        ret = (ret << 8) + (tmp[1] & 0xff);
        ret = (ret << 8) + (tmp[2] & 0xff);
        ret = (ret << 8) + (tmp[3] & 0xff);

        return ret;
    }

    miniutil::m_uint64_t get_uint64_be(const char* buf)
    {
        miniutil::m_uint64_t ret;
        unsigned char *tmp;

        tmp = (unsigned char *) buf;

        ret = tmp[0] & 0xff;
        ret = (ret << 8) + (tmp[1] & 0xff);
        ret = (ret << 8) + (tmp[2] & 0xff);
        ret = (ret << 8) + (tmp[3] & 0xff);
        ret = (ret << 8) + (tmp[4] & 0xff);
        ret = (ret << 8) + (tmp[5] & 0xff);
        ret = (ret << 8) + (tmp[6] & 0xff);
        ret = (ret << 8) + (tmp[7] & 0xff);

        return ret;
    }

    miniutil::m_uint16_t get_uint16_le(const char* buf)
    {
        miniutil::m_uint16_t ret;
        unsigned char *tmp;

        tmp = (unsigned char *) buf;

        ret = tmp[1] & 0xff;
        ret = (ret << 8) + (tmp[0] & 0xff);
        return ret;
    }

    miniutil::m_uint32_t get_uint32_le(const char* buf)
    {
        miniutil::m_uint32_t ret;
        unsigned char *tmp;

        tmp = (unsigned char *) buf;

        ret = tmp[3] & 0xff;
        ret = (ret << 8) + (tmp[2] & 0xff);
        ret = (ret << 8) + (tmp[1] & 0xff);
        ret = (ret << 8) + (tmp[0] & 0xff);

        return ret;
    }

    miniutil::m_uint64_t get_uint64_le(const char* buf)
    {
        miniutil::m_uint64_t ret;
        unsigned char *tmp;

        tmp = (unsigned char *) buf;

        ret = tmp[7] & 0xff;
        ret = (ret << 8) + (tmp[6] & 0xff);
        ret = (ret << 8) + (tmp[5] & 0xff);
        ret = (ret << 8) + (tmp[4] & 0xff);
        ret = (ret << 8) + (tmp[3] & 0xff);
        ret = (ret << 8) + (tmp[2] & 0xff);
        ret = (ret << 8) + (tmp[1] & 0xff);
        ret = (ret << 8) + (tmp[0] & 0xff);

        return ret;
    }

    void put_uint16_be(char* buf, miniutil::m_uint16_t value)
    {
        unsigned char *tmp;

        tmp = (unsigned char *) buf;

        tmp[1] = value & 0xff;
        tmp[0] = (value >>= 8) & 0xff;
    }

    void put_uint32_be(char* buf, miniutil::m_uint32_t value)
    {
        unsigned char *tmp;

        tmp = (unsigned char *) buf;

        tmp[3] = value & 0xff;
        tmp[2] = (value >>= 8) & 0xff;
        tmp[1] = (value >>= 8) & 0xff;
        tmp[0] = (value >>= 8) & 0xff;
    }

    void put_uint64_be(char* buf, miniutil::m_uint64_t value)
    {
        unsigned char *tmp;

        tmp = (unsigned char *) buf;

        tmp[7] = value & 0xff;
        tmp[6] = (value >>= 8) & 0xff;
        tmp[5] = (value >>= 8) & 0xff;
        tmp[4] = (value >>= 8) & 0xff;
        tmp[3] = (value >>= 8) & 0xff;
        tmp[2] = (value >>= 8) & 0xff;
        tmp[1] = (value >>= 8) & 0xff;
        tmp[0] = (value >>= 8) & 0xff;
    }

    void put_uint16_le(char* buf, miniutil::m_uint16_t value)
    {
        unsigned char *tmp;

        tmp = (unsigned char *) buf;

        tmp[0] = value & 0xff;
        tmp[1] = (value >>= 8) & 0xff;
    }

    void put_uint32_le(char* buf, miniutil::m_uint32_t value)
    {
        unsigned char *tmp;

        tmp = (unsigned char *) buf;

        tmp[0] = value & 0xff;
        tmp[1] = (value >>= 8) & 0xff;
        tmp[2] = (value >>= 8) & 0xff;
        tmp[3] = (value >>= 8) & 0xff;
    }

    void put_uint64_le(char* buf, miniutil::m_uint64_t value)
    {
        unsigned char *tmp;

        tmp = (unsigned char *) buf;

        tmp[0] = value & 0xff;
        tmp[1] = (value >>= 8) & 0xff;
        tmp[2] = (value >>= 8) & 0xff;
        tmp[3] = (value >>= 8) & 0xff;
        tmp[4] = (value >>= 8) & 0xff;
        tmp[5] = (value >>= 8) & 0xff;
        tmp[6] = (value >>= 8) & 0xff;
        tmp[7] = (value >>= 8) & 0xff;
    }


    Pickler::Pickler(const char* pcontent, int len)
    {
        mctlen = len;
        mroffset = 0;
        mpcontent = pcontent;		
    }

    Pickler::~Pickler()
    {
    }

	int Pickler::PopFront(char* pvalue)
    {
        if(pvalue == NULL)
            return -1;

	    if((mctlen-mroffset) < sizeof(char))
		    return -1;
	    *pvalue = *(mpcontent+mroffset);
	    mroffset += sizeof(char);
	    return 0;
    }

	int Pickler::PopFront(short* pvalue)
    {
        if(pvalue == NULL)
            return -1;
	    if((mctlen-mroffset) < sizeof(short))
		    return -1;

#ifdef SOFT_ALIGEMENT
	    unsigned short cvalue;
	    memcpy((char*)&cvalue, mpcontent+mroffset, sizeof(unsigned short));
	    *pvalue = ntohs(cvalue);
#else
	    *pvalue = ntohs(*(short*)(mpcontent+mroffset));
#endif
	    mroffset += sizeof(short);
	    return 0;

    }

	int Pickler::PopFront(int* pvalue)
    {
        if(pvalue == NULL)
            return -1;
	    if((mctlen-mroffset) < sizeof(int))
		    return -1;
#ifdef SOFT_ALIGEMENT
	    unsigned int cvalue;
	    memcpy((char*)&cvalue, mpcontent+mroffset, sizeof(unsigned int));
	    *pvalue = ntohl(cvalue);
#else
	    *pvalue = ntohl(*(int*)(mpcontent+mroffset));
#endif
	    mroffset += sizeof(int);
	    return 0;

    }

    int Pickler::PopFront(long long* pvalue)
    {
        if(pvalue == NULL)
            return -1;
	    if((mctlen-mroffset) < sizeof(long long))
		    return -1;
        int be32, le32;
        int ret = PopFront(&be32);
        ret = PopFront(&le32);

        long long vle = be32;
        vle = (vle<<32);
        vle += le32;

        *pvalue = vle;
        return 0;
    }

	int Pickler::PopFront(char* pvalue, int len)
    {
        if(pvalue == NULL)
            return -1;
	    if((mctlen-mroffset) < (int)len)
		    return -1;
	    memcpy(pvalue, mpcontent+mroffset, len);
	    mroffset += len;
	    return 0;
    }

	int Pickler::PopFront(std::string * pvalue)
    {
        if(pvalue == NULL)
            return -1;
        int ret = 0;
        int len = 0;
        ret = PopFront(&len);
        if(ret == -1)
            return -1;
	    if((mctlen-mroffset) < (int)len)
		    return -1;
        pvalue->assign(mpcontent+mroffset, len);
        mroffset += len;
        return 0;
    }

    int Pickler::SeekReadPos(int flag, int offset)
    {
        if(flag == PICKLE_SET)
        {
            if(offset < 0 || offset > mctlen)
                return -1;
            mroffset = offset;
        }
        else if(flag == PICKLE_CUR)
        {
            if(offset + mroffset > mctlen ||
                offset + mroffset < 0)
                return -1;
            mroffset += offset;
        }
        else if(flag == PICKLE_END)
        {
            if(offset > 0 || mctlen + offset < 0)
                return -1;
            mroffset = mctlen + offset;
        }
        else
        {
            return -1;
        }
        return 0;
    }

    //-------------------------------------------------------------
    Picklew::Picklew()
    {
        mselfalloc = true;
        mctlen = 0 ;
        mbflen = 1024;
        mpcontent = new char[mbflen];
    }

    Picklew::Picklew(char* pcontent, int len)
    {
        mselfalloc = false;
        mctlen = 0 ;
        mbflen = len;
        mpcontent = pcontent;
    }

    Picklew::~Picklew()
    {
        if(mselfalloc)
            delete []mpcontent;
    }


    int Picklew::PushBack(char value)
    {
        int ret = CheckRightLenOver(sizeof(value));
        if(ret == -1)
            return -1;

	    *((char*)(mpcontent + mctlen))  = value;
	    mctlen += sizeof(char);
	    return mctlen;
    	
    }
	
    int Picklew::PushBack(short value)
    {
        int ret = CheckRightLenOver(sizeof(value));
        if(ret == -1)
            return -1;

    #ifdef SOFT_ALIGEMENT
	    short cvalue = htons(value);
	    memcpy(mpcontent + mctlen, (char*)&cvalue, sizeof(short));
    #else
	    *((unsigned short*)(mpcontent + mctlen))  = htons(value);
    #endif
	    mctlen += sizeof(short);
	    return mctlen;
    		
    }

    int Picklew::PushBack(int value)
    {
        int ret = CheckRightLenOver(sizeof(value));
        if(ret == -1)
            return -1;

    #ifdef SOFT_ALIGEMENT
	    int cvalue = htonl(value);
	    memcpy(mpcontent + mctlen, (char*)&cvalue, sizeof(int));
    #else
	    *((int*)(mpcontent + mctlen))  = htonl(value);
    #endif

	    mctlen += sizeof(int);
	    return mctlen;
    }

    int Picklew::PushBack(long long value)
    {
        int ret = CheckRightLenOver(sizeof(value));
        if(ret == -1)
            return -1;

        int be32, le32;
        be32 = (int)(value>>32)&0xffffffff;
        le32 = (int)(value&0xffffffff);
        PushBack(be32);
        PushBack(le32);

        return mctlen;
    }

    int Picklew::PushBack(const char *pvalue, int len)
    {
        int ret = CheckRightLenOver(len);
        if(ret == -1)
            return -1;

	    if(pvalue == NULL)
		    memset(mpcontent + mctlen, 0x00, len);
	    else
		    memcpy(mpcontent + mctlen, pvalue, len);
	    mctlen += len;
	    return mctlen;
    }
	

    //set value
    int Picklew::SetValue(int offset, char value)
    {
	    if(offset + (int)(sizeof(value)) >= mctlen)
		    return -1;
	    *((char*)(mpcontent + offset))  = value;
	    return 0;
    }

    int Picklew::SetValue(int offset, short value)
    {
	    if(offset + (int)(sizeof(value)) >= mctlen)
		    return -1;
    #ifdef SOFT_ALIGEMENT
	    unsigned short cvalue = htons(value);
	    memcpy(mpcontent + offset, (char*)&cvalue, sizeof(short));
    #else
	    *((unsigned short*)(mpcontent + offset))  = htons(value);
    #endif

	    return 0;
    }

    int Picklew::SetValue(int offset, int value)
    {
	    if(offset + (int)(sizeof(value)) >= mctlen)
		    return -1;
    #ifdef SOFT_ALIGEMENT
	    unsigned int cvalue = htonl(value);
	    memcpy(mpcontent + offset, (char*)&cvalue, sizeof(int));
    #else
	    *((unsigned int*)(mpcontent + offset))  = htonl(value);
    #endif
	    return 0;
    }

    int Picklew::SetValue(int offset, const char* pvalue, int len)
    {
	    if(offset + (int)len >= mctlen)
		    return -1;
	    if(pvalue == NULL)
		    memset((char*)(mpcontent + offset), 0x00, len);
	    else
		    memcpy((char*)(mpcontent + offset), pvalue, len);
	    return 0;
    }

    //Refer the inner content
    const char* Picklew::RefData()
    {
	    return mpcontent;
    }

    const int Picklew::RefDataLen()
    {
	    return mctlen;
    }

    int Picklew::CheckRightLenOver(int inclen)
    {
        if(!mselfalloc)
        {
            if(mctlen + inclen > mbflen)
                return -1;
            else
                return 0;
        }

	    int newbuflen = mbflen;
	    char* newpcontent;
	    if(inclen <= 0)
		    return 0;
	    else if(inclen > 0 && mctlen + inclen > mbflen)
	    {
		    if(mctlen + inclen > (((unsigned int)0x1)<<30))
			    return -1;
		    while( 1)
		    {
			    newbuflen =  newbuflen<<1;
			    if(newbuflen > mctlen + inclen)
			    {
				    newpcontent = new char[newbuflen];
				    memset(newpcontent, 0x00, newbuflen);
				    memcpy(newpcontent, mpcontent, mbflen);
				    delete []mpcontent;
				    mpcontent = newpcontent;
				    mbflen = newbuflen;
				    break;
			    }
		    }
	    }
	    return 0;
    }

} }

